﻿using System;
using System.Diagnostics;
using System.IO;
using System.Xml.Serialization;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;

namespace DE.Sdc.SharePoint.Common.Deployment
{
    public class AppConfigModifier
    {
        private readonly SPFeatureReceiverProperties _receiverProperties;
        private SPWeb _web = null;
        private readonly string _appConfigModificationsXmlPath;
        private readonly bool _removeModifications;
        private readonly bool _deleteHttpContextBeforeUpdate;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="appConfigModificationsXmlPath">Path to the appconfig modification xml</param>
        /// <param name="receiverProperties">The feature receiver properties</param>
        /// <param name="removeModifications">Indicates whether the appconfig modification will be removed or not</param>
        private AppConfigModifier(string appConfigModificationsXmlPath, SPFeatureReceiverProperties receiverProperties, bool removeModifications)
        {
            _appConfigModificationsXmlPath = appConfigModificationsXmlPath;
            _receiverProperties = receiverProperties;
            _removeModifications = removeModifications;
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="appConfigModificationsXmlPath">Path to the appconfig modification xml</param>
        /// <param name="web">The web where the feature is deployed</param>
        /// <param name="removeModifications">Indicates whether the appconfig modification will be removed or not</param>
        /// <param name="deleteHttpContextBeforeUpdate">Indicates whether the http context will be removed or not</param>
        private AppConfigModifier(string appConfigModificationsXmlPath, SPWeb web, bool removeModifications, bool deleteHttpContextBeforeUpdate)
        {
            _appConfigModificationsXmlPath = appConfigModificationsXmlPath;
            _web = web;
            _removeModifications = removeModifications;
            _deleteHttpContextBeforeUpdate = deleteHttpContextBeforeUpdate;
        }

        /// <summary>
        /// Executes the appconfig modifications
        /// </summary>
        private void Execute()
        {
            AppConfigModifications modifications = null;
            var serializer = new XmlSerializer(typeof (AppConfigModifications));
            FileStream modificationXml = System.IO.File.OpenRead(_appConfigModificationsXmlPath);

            try
            {
                modifications = (AppConfigModifications) serializer.Deserialize(modificationXml);
            }
            finally
            {
                modificationXml.Close();
            }

            SPWeb featureWeb = _web ?? DeploymentHelper.GetWeb(_receiverProperties.Feature.Parent);
            Guid webId = featureWeb.ID;
            Guid siteId = featureWeb.Site.ID;

            SPSecurity.RunWithElevatedPrivileges(delegate()
                                                     {
                                                         using (SPSite elevatedSite = new SPSite(siteId))
                                                         {
                                                             SPWeb elevatedWeb = elevatedSite.OpenWeb(webId);
                                                             Trace.WriteLine("Begin RunWithElevatedPriviliges");
                                                             Modify(elevatedWeb, modifications);
                                                             Trace.WriteLine("End RunWithElevatedPriviliges");
                                                         }
                                                     });

        }

        /// <summary>
        /// Modifies the appconfig
        /// </summary>
        /// <param name="featureWeb">The web where the feature is deployed</param>
        /// <param name="modifications">Xml object for the appconfig modifications</param>
        public void Modify(SPWeb featureWeb, AppConfigModifications modifications)
        {
            SPWebApplication webApplication = featureWeb.Site.WebApplication;

            for (int index = 0; index < modifications.Modifications.Length; index++)
            {
                var modificationDefinition = modifications.Modifications[index];
                var modification = new SPWebConfigModification(modificationDefinition.Name,
                                                               modificationDefinition.XPath)
                {
                    Owner = modificationDefinition.Owner,
                    Sequence = modificationDefinition.Sequence,
                    Type = modificationDefinition.Type,
                    Value = modificationDefinition.XmlValue != null
                                ?

                                    modificationDefinition.XmlValue.OuterXml.Replace(
                                        "xmlns=\"" + modificationDefinition.XmlValue.NamespaceURI +
                                        "\"", "")
                                : !string.IsNullOrEmpty(modificationDefinition.TextValue)
                                      ? modificationDefinition.TextValue.Replace("\n", "").Trim()
                                      : string.Empty
                };

                if (_removeModifications)
                    webApplication.WebConfigModifications.Remove(modification);
                else
                    webApplication.WebConfigModifications.Add(modification);
            }

            if (_deleteHttpContextBeforeUpdate)
                System.Web.HttpContext.Current = null;
            try
            {
                webApplication.WebService.ApplyWebConfigModifications();
            }
            catch
            {
            }
            webApplication.Update(true);
        }

        /// <summary>
        /// Executes the appconfig modifications
        /// </summary>
        /// <param name="appConfigDescriptionXmlPath">Path to the appconfig modification xml</param>
        /// <param name="receiverProperties">The feature receiver properties</param>
        /// <param name="removeModifications">Indicates whether the appconfig modification will be removed or not</param>
        public static void Execute(string appConfigDescriptionXmlPath, SPFeatureReceiverProperties receiverProperties, bool removeModifications)
        {
            var modifier = new AppConfigModifier(appConfigDescriptionXmlPath,
                                                 receiverProperties,
                                                 removeModifications);
            modifier.Execute();
        }

        /// <summary>
        /// Executes the appconfig modifications
        /// </summary>
        /// <param name="appConfigDescriptionXmlPath">Path to the appconfig modification xml</param>
        /// <param name="web">The web where the feature is deployed</param>
        /// <param name="removeModifications">Indicates whether the appconfig modification will be removed or not</param>
        /// <param name="deleteHttpContextBeforeUpdate">Indicates whether the http context will be removed or not</param>
        public static void Execute(string appConfigDescriptionXmlPath, SPWeb web, bool removeModifications, bool deleteHttpContextBeforeUpdate)
        {
            var modifier = new AppConfigModifier(appConfigDescriptionXmlPath,
                                                 web,
                                                 removeModifications,
                                                 deleteHttpContextBeforeUpdate);
            modifier.Execute();
        }
    }
}
